--
-- This file contains operations on bags/items that are not really part of 
-- Baggins core functionality, such as compressing your inventory, splitting
-- items, etc.
--

local Baggins = Baggins

local pt = LibStub("LibPeriodicTable-3.1", true)
local L = AceLibrary("AceLocale-2.2"):new("Baggins")
local dewdrop = AceLibrary("Dewdrop-2.0")



local bankBags = { BANK_CONTAINER }
for i=NUM_BAG_SLOTS+1, NUM_BAG_SLOTS+NUM_BANKBAGSLOTS do
	tinsert(bankBags, i);
end

local charBags = {}
for i=0, NUM_BAG_SLOTS do
	tinsert(charBags, i);
end

local itemPTSet_For_ContainerPTSet = {
	["Misc.Bag.Special.Ammo"] = "Misc.Reagent.Ammo.Bullet",
	["Misc.Bag.Special.Enchanting"] = "Misc.Container.ItemsInType.Enchanting",
	["Misc.Bag.Special.Engineering"] = "Misc.Container.ItemsInType.Engineering",
	["Misc.Bag.Special.Herb"] = "Misc.Container.ItemsInType.Herb",
	["Misc.Bag.Special.Jewelcrafting"] = "Misc.Container.ItemsInType.Gem",
	["Misc.Bag.Special.Mining"] = "Misc.Container.ItemsInType.Mining",
	["Misc.Bag.Special.Quiver"] = "Misc.Reagent.Ammo.Arrow",
	["Misc.Bag.Special.Soul Shard"] = "Misc.Container.ItemsInType.Soul Shard"
}


------------------------------------------------------
-- Compress stacks and move stuff to specialty bags --
------------------------------------------------------


local incompleteSlots = {}	-- pairs of itemlink = bag*1000+slot, e.g. "foo" = 207 (bag 2 slot 7)
local compressLoopProtect = 0

function Baggins:CompressBags(bank,testonly)
	local bags = bank and bankBags or charBags;
	
	if not testonly and CursorHasItem() then 
		if not self.compress_target_bag then
			return; -- oookay, i'm not touching that item, it wasn't i that picked it up
		end
		PickupContainerItem(self.compress_target_bag, self.compress_target_slot);
		self:ScheduleEvent("Baggins_CompressBags", self.CompressBags, 0.1, self, bank);
		return;
	end
	self.compress_target_bag = nil;
	
	for k,v in pairs(incompleteSlots) do
		incompleteSlots[k] = nil;
	end
	
	for _,bag in ipairs(bags) do
		for slot=1,(GetContainerNumSlots(bag) or -1) do
			local texture, itemCount, locked, quality, readable = GetContainerItemInfo(bag, slot);
			local link = GetContainerItemLink(bag, slot)
			if link then
				local sName, sLink, iRarity, iLevel, iMinLevel, sType, sSubType, iMaxStack = GetItemInfo(link);
				if sName then
					if itemCount < iMaxStack then
						local itemid = link:match("item:(-?[%d]+):");
						if(incompleteSlots[itemid]) then	-- see if we have an incomplete stack of this sitting around
							if testonly then return true; end	-- Yup, we've got something that needs compressing!
							compressLoopProtect = compressLoopProtect + 1;
							if compressLoopProtect > 100 then compressLoopProtect=0; return; end
							
							PickupContainerItem(floor(incompleteSlots[itemid]/1000), incompleteSlots[itemid]%1000);
							self.compress_target_bag = bag
							self.compress_target_slot = slot
							self:ScheduleEvent("Baggins_CompressBags", self.CompressBags, 0.3, self, bank);
							return;
						else
							incompleteSlots[itemid] = bag*1000 + slot;	-- remember that there's an incomplete stack of this item sitting around
						end
					end
				end
			end				
		end
	end
	
	return Baggins:MoveToSpecialtyBags(bank,testonly);
end

local specialtyTargetBags = {}	-- "PTSet.Category.Subcat" = bag*1000 + slot

function Baggins:MoveToSpecialtyBags(bank,testonly)
	if not pt then return end
	
	if not testonly and CursorHasItem() then 
		if not self.compress_target_bag then
			return; -- oookay, i'm not touching that item, it wasn't i that picked it up
		end
		PickupContainerItem(self.compress_target_bag, self.compress_target_slot);
		self:ScheduleEvent("Baggins_CompressBags", self.MoveToSpecialtyBags, 0.1, self, bank);
		return;
	end
	self.compress_target_bag = nil;
	
	-- Find specialty targets
	for k,v in pairs(specialtyTargetBags) do
		specialtyTargetBags[k] = nil;
	end
	for _,bag in ipairs(bank and bankBags or charBags) do
		if bag>0 and (GetContainerNumSlots(bag) or 0)>0 then 
			local _,containerPTSet = pt:ItemInSet(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)), "Misc.Bag.Special");
			if(containerPTSet) then
				for slot=1, (GetContainerNumSlots(bag) or 0) do
					if not GetContainerItemLink(bag, slot) then
						specialtyTargetBags[itemPTSet_For_ContainerPTSet[containerPTSet] or "UNKNOWN"] =	-- only time we'd get unknown is when new bag types are added, and then we don't know what can go in them
							bag*1000 + slot;
						break;
					end
				end
			end
		end
	end
	
	-- Find stuff that can go in specialty bags
	if #specialtyTargetBags then
		for _,bag in ipairs(bank and bankBags or charBags) do
			local _,containerPTSet;
			if bag>0 and (GetContainerNumSlots(bag) or 0)>0 then
			 	_,containerPTSet = pt:ItemInSet(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)), "Misc.Bag.Special");
			end
			
			if not containerPTSet then
				for slot=1, (GetContainerNumSlots(bag) or 0) do
					local link = GetContainerItemLink(bag, slot)
					if link then
						local itemid = link:match("item:(-?[%d]+):");
						for ptset,dest in pairs(specialtyTargetBags) do
							if pt:ItemInSet(itemid, ptset) then
								if testonly then return true; end
								compressLoopProtect = compressLoopProtect + 1;
								if compressLoopProtect > 100 then compressLoopProtect=0; return; end
								
								PickupContainerItem(bag,slot);
								self.compress_target_bag = floor(dest/1000);
								self.compress_target_slot = dest%1000;
								self:ScheduleEvent("Baggins_CompressBags", self.MoveToSpecialtyBags, 0.3, self, bank);
								return;
							end
						end
					end
				end
			end
		end
	end

	if not testonly then
		compressLoopProtect = 0;
	end
	
end



Baggins:RegisterSignal("Baggins_AllBagsClosed", 
	function(self)
		self:CancelScheduledEvent("Baggins_CompressBags");
		compressLoopProtect = 0
	end,
	Baggins
);
	

local lastCanCompressBags = "maybe";
local lastCanCompressBank = "maybe";

function Baggins:RecheckCompress(delay)
	if delay then
		self:ScheduleEvent("Baggins_RecheckCompress", self.RecheckCompress, delay, self, nil);
		return;
	end
	local b
	b = self:CompressBags(false, true);	-- false=bags, true=testonly
	if b ~= lastCanCompressBags then
		lastCanCompressBags = b;
		self:FireSignal("Baggins_CanCompress", false, b);		-- false=bags
	end
	b = self:CompressBags(true, true);	-- true=bank, true=testonly
	if b ~= lastCanCompressBank then
		lastCanCompressBank = b;
		self:FireSignal("Baggins_CanCompress", true, b);  -- true=bank
	end
end

Baggins:RegisterSignal("Baggins_BagsUpdatedWhileOpen",
	function(self)
		self:RecheckCompress(compressLoopProtect>0 and 1.0 or 0.2);
	end,
	Baggins
);

Baggins:RegisterSignal("Baggins_BagOpened",
	function(self)
		if not pt then 
			pt = LibStub("LibPeriodicTable-3.1", true)
		end
		self:RecheckCompress(0.1)
	end,
	Baggins
);



---------------------
-- Splitting items --
---------------------

local function BagginsItemButton_Split(bag,slot,amount)
	-- Pick up the new stack
	SplitContainerItem(bag, slot, amount or 1)

	-- First, make a list of bags to try and put the split-off stack in. 
	local bags = {}					-- Specialty bags FIRST so we test them before regular bags.
	local ptsetforbag = {} 	-- Also remember which itemset can go into a given bag
	
	for _,b in ipairs(Baggins:IsBankBag(bag) and bankBags or charBags) do
		if (GetContainerNumSlots(b) or 0) < 1 then
			-- noop
		elseif(b>0) then 
			local containerPTSet
			if pt then
				containerPTSet = select(2,pt:ItemInSet(GetInventoryItemLink("player", ContainerIDToInventoryID(b)), "Misc.Bag.Special"));
			end
			if(containerPTSet) then
				ptsetforbag[b] = itemPTSet_For_ContainerPTSet[containerPTSet] or "UNKNOWN";	-- only time we'd get unknown is when new bag types are added, and then we don't know what can go in them
				tinsert(bags, 1, b);	-- stick first
			else
				tinsert(bags, b);	-- stick last
			end
		else
			tinsert(bags, b);	-- stick last
		end
	end
	
	if(IsShiftKeyDown()) then
		return;	-- Just split off if shift was held down, keep new stack in cursor
	end

	-- Find somewhere	to put it!
	local itemid = tonumber( GetContainerItemLink(bag,slot):match("item:(-?%d+):") );
	assert(itemid);
	
	for _,destbag in ipairs(bags) do
		if pt and ptsetforbag[destbag] and not pt:ItemInSet(itemid, ptsetforbag[destbag]) then
			-- Baggins:Print(itemid, "can't go in bag", destbag, " - it only holds ", ptsetforbag[destbag]);
		else
			for slot=1, GetContainerNumSlots(destbag) do
				if not GetContainerItemLink(destbag, slot) then	
					PickupContainerItem(destbag, slot) 
					return
				end
			end
		end
	end
	-- Ends up with the item in the cursor if there's no room. Indication enough?
end



---------------------
-- ItemButton menu --
---------------------

local lastSplitSliderValue = 1

local function splitSliderFunc(value)
	lastSplitSliderValue = value;
end

Baggins:RegisterSignal("Baggins_ItemButtonMenu", 

	function(self, button, dewdrop, level, value)
		local bag = button:GetParent():GetID();
		local slot = button:GetID();
		local link = GetContainerItemLink(bag,slot)
		if not link then return; end
		local _, itemCount = GetContainerItemInfo(bag, slot);
		local itemstring = link:match("(item:[-%d:]+)");
	
		if level==1 then
			local b;
			
			-- "Use"
			if not Baggins:IsBankBag(bag) then
				dewdrop:AddLine("text",L["Use"], "secure", { type="item", item=itemstring },
					"closeWhenClicked",true, "tooltipText", L["Use/equip the item rather than bank/sell it"]);
				b=true;
			end
			
			-- "Split"
			if itemCount>1 then
				dewdrop:AddLine("text",format(L["Split %d"],lastSplitSliderValue),
					"func", BagginsItemButton_Split,
					"arg1",bag, "arg2",slot, "arg3", lastSplitSliderValue,
					"tooltipText", L["Split_tooltip"], 
					"mouseoverUnderline", true,
					"hasArrow",true, "hasSlider", true,
					"sliderMin", 1, "sliderMax", itemCount-1,
					"sliderStep", 1,
					"sliderValue", lastSplitSliderValue,
					"sliderFunc", splitSliderFunc,
					"textR", 1,
					"textG", lastSplitSliderValue>=itemCount and 0.5 or 1,
					"textB", lastSplitSliderValue>=itemCount and 0.5 or 1
				);
				b=true;
			end
			
			-- separator
			if b then
				dewdrop:AddLine("text","","disabled",1);
			end
		
		end
		
	end,
	Baggins
);

Baggins:RegisterSignal("Baggins_BagsUpdatedWhileOpen",
	function(self)
		if dewdrop:GetOpenedParent() then
			dewdrop:Refresh(1);
		end
	end,
	Baggins
);
